package com.zgw.fireline.base.widgets;

import org.eclipse.jface.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

/**
 * The hover used to show a decoration image's description.
 */
public class ToolTip {
	private static final String EMPTY = ""; //$NON-NLS-1$

	/**
	 * Offset of info hover arrow from the left or right side.
	 */
	private int hao = 10;

	/**
	 * Width of info hover arrow.
	 */
	private int haw = 8;

	/**
	 * Height of info hover arrow.
	 */
	private int hah = 10;

	/**
	 * Margin around info hover text.
	 */
	private int hm = 2;

	/**
	 * This info hover's shell.
	 */
	Shell hoverShell;

	/**
	 * The info hover text.
	 */
	String text = EMPTY;

	/**
	 * The region used to manage the shell shape
	 */
	Region region;

	/**
	 * Boolean indicating whether the last computed polygon location had an
	 * arrow on left. (true if left, false if right).
	 */
	boolean arrowOnLeft = true;

	Point location; // 基于控件的位置
	private Control control;

	public static void openToolTip(String text, Control control, int x, int y) {
		ToolTip tip = new ToolTip(control.getShell(), control);
		tip.location = new Point(x, y);
		tip.setText(text);
		tip.setVisible(true);
	}

	// 测试方法
	public static void main(String[] args) {
		Shell s = new Shell();
		final Button but = new Button(s, SWT.NONE | SWT.APPLICATION_MODAL);
		but.setBounds(10, 10, 50, 20);
		but.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				ToolTip.openToolTip("ab\r\n\r\n\r\ndadsfc", (Control) e.widget,
						0, 0);
			}
		});
		s.open();
		while (!s.isDisposed()) {
			if (!s.getDisplay().readAndDispatch()) {
				s.getDisplay().sleep();
			}
		}
	}

	/*
	 * Create a hover parented by the specified shell.
	 */
	public ToolTip(final Shell parent, Control control) {
		final Display display = parent.getDisplay();
		this.control = control;
		hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_FOCUS
				| SWT.TOOL);
		hoverShell.setBackground(display
				.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
		hoverShell.setForeground(display
				.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
		hoverShell.addPaintListener(new PaintListener() {
			public void paintControl(PaintEvent pe) {
				pe.gc.drawText(text, hm, hm);
				if (!Util.isMac()) {
					pe.gc.drawPolygon(getPolygon(true));
				}
			}
		});
		final Listener closeListener = new Listener() {
			public void handleEvent(Event event) {
				if (event.type == SWT.Move) {
					setText(text);
				} else {
					dispose();
				}

			}
		};
		// hoverShell.addListener(SWT.MouseDown, closeListener);
		parent.getDisplay().addFilter(SWT.MouseDown, closeListener);
		parent.addListener(SWT.Move, closeListener);
		parent.addListener(SWT.Deactivate, closeListener);
		hoverShell.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				parent.getDisplay().removeFilter(SWT.MouseDown, closeListener);
				parent.removeListener(SWT.Move, closeListener);
				parent.removeListener(SWT.Deactivate, closeListener);
			}
		});
	}

	/*
	 * Compute a polygon that represents a hover with an arrow pointer. If
	 * border is true, compute the polygon inset by 1-pixel border. Consult the
	 * arrowOnLeft flag to determine which side the arrow is on.
	 */
	int[] getPolygon(boolean border) {
		Point e = getExtent();
		int b = border ? 1 : 0;
		if (arrowOnLeft) {
			return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b, hao + haw,
					e.y - b, hao + haw / 2, e.y + hah - b, hao, e.y - b, 0,
					e.y - b, 0, 0 };
		}
		return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b, e.x - hao - b,
				e.y - b, e.x - hao - haw / 2, e.y + hah - b, e.x - hao - haw,
				e.y - b, 0, e.y - b, 0, 0 };
	}

	/*
	 * Dispose the hover, it is no longer needed. Dispose any resources
	 * allocated by the hover.
	 */
	void dispose() {
		if (!hoverShell.isDisposed()) {
			hoverShell.dispose();
		}
		if (region != null) {
			region.dispose();
		}
	}

	/*
	 * Set the visibility of the hover.
	 */
	void setVisible(boolean visible) {
		if (visible) {
			if (!hoverShell.isVisible()) {
				hoverShell.setVisible(true);
			}
		} else {
			if (hoverShell.isVisible()) {
				hoverShell.setVisible(false);
			}
		}
	}

	/*
	 * Set the text of the hover to the specified text. Recompute the size and
	 * location of the hover to hover near the decoration rectangle, pointing
	 * the arrow toward the target control.
	 */
	void setText(String t) {
		if (t == null) {
			t = EMPTY;
		}
		if (!t.equals(text)) {
			Point oldSize = getExtent();
			text = t;
			hoverShell.redraw();
			Point newSize = getExtent();
			if (!oldSize.equals(newSize)) {
				setNewShape();
			}
		}
		Point extent = getExtent();
		int y = (-extent.y - hah + 1) + location.y;
		int x = (arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2)
				+ location.x;
		hoverShell.setLocation(control.toDisplay(x, y));
		// hoverShell.setLocation(location.x + x, location.y + y);
	}

	/*
	 * Return whether or not the hover (shell) is visible.
	 */
	boolean isVisible() {
		return hoverShell.isVisible();
	}

	/*
	 * Compute the extent of the hover for the current text.
	 */
	Point getExtent() {
		GC gc = new GC(hoverShell);
		Point e = gc.textExtent(text);
		gc.dispose();
		e.x += hm * 2;
		e.y += hm * 2;
		return e;
	}

	/*
	 * Compute a new shape for the hover shell.
	 */
	void setNewShape() {
		Region oldRegion = region;
		region = new Region();
		region.add(getPolygon(false));
		hoverShell.setRegion(region);
		if (oldRegion != null) {
			oldRegion.dispose();
		}
	}
}